﻿using System.Dynamic;

namespace IOCDemo.IocVersion2;

/// <summary>
/// 服务描述类
/// </summary>
public class ServiceDescriptor
{
    /// <summary>
    /// 唯一标识
    /// </summary>
    public string Id { get; set; }

    /// <summary>
    /// 输入标识
    /// </summary>
    public string Tag { set; get; }

    /// <summary>
    /// 源类型(接口)
    /// </summary>
    public Type Source { set; get; }

    /// <summary>
    /// 目标类
    /// </summary>
    public Type TargetService { set; get; }

    /// <summary>
    /// 生命周期类型
    /// </summary>
    public LifeTimeType ServiceType { set; get; }

    /// <summary>
    /// 默认使用的构造方法
    /// </summary>
    internal ConstructorModel Default = null;

    /// <summary>
    /// 构造方法的属性集合
    /// </summary>
    internal List<ConstructorModel> Constructors { set; get; }

    /// <summary>
    /// 依赖或者需要注入的服务对象
    /// </summary>
    internal List<ServiceDescriptor> DependServices { set; get; }

    /// <summary>
    /// 单例模式唯一对象
    /// </summary>
    public object Instance { set; get; }

    private void InitConstructorModels()
    {
        Constructors = new List<ConstructorModel>();
        DependServices = new List<ServiceDescriptor>();
        var infos = TargetService.GetConstructors();
        foreach (var info in infos)
        {
            var param = info.GetParameters();
            var model = new ConstructorModel()
            {
                Info = info,
                Order = param.Length,
                ParameterInfos = param
            };
            Constructors.Add(model);
        }

        Constructors.Sort((x, y) => x.Order.CompareTo(y.Order));
    }

    public ServiceDescriptor(Type type, LifeTimeType serviceType = LifeTimeType.Normal) : this(type, type, serviceType)
    {
        Source = type;
        ServiceType = serviceType;
    }

    public ServiceDescriptor(Type source, Type targetService, LifeTimeType serviceType = LifeTimeType.Normal,
        string tag = "")
    {
        Tag = tag;
        Source = source;
        TargetService = targetService;
        ServiceType = serviceType;
        Id = source.Name + tag;
        InitConstructorModels();
    }

    public object GetService()
    {
        switch (ServiceType)
        {
            case LifeTimeType.Normal:
                return CreateInstance(TargetService);
            case LifeTimeType.Singleton:
                if (Instance == null)
                {
                    Instance = CreateInstance(TargetService);
                }

                return Instance;
            default:
                return CreateInstance(TargetService);
        }
    }

    private object CreateInstance(Type targetService)
    {
        if (Default == null) return System.Activator.CreateInstance(targetService);
        var paramters = new List<object>();
        foreach (var item in Default.ParameterInfos)
        {
            var service = DependServices.Find(x => x.Source == item.ParameterType);
            if (service==null)
            {
                return System.Activator.CreateInstance(targetService);
            }
            paramters.Add(service.GetService());
        }
        return Default.Info.Invoke(paramters.ToArray());
    }
}